package com.gwt.auth.service.impl;

import com.gwt.auth.config.AuthFilterConfig;
import com.gwt.auth.service.AuthService;
import com.gwt.auth.vo.AuthUser;
import com.gwt.boot.exception.BusinessException;
import com.gwt.boot.tools.IpTools;
import com.gwt.boot.tools.JwtTools;
import com.gwt.cloud.constant.ApiResultEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Service
@EnableCaching
public class AuthServiceImpl implements AuthService {

    /**
     * 用户信息
     */
    private static final String AUTH_USER = AuthFilterConfig.AUTH_REDIS_ROOT_KEY + "USER";

    /**
     * 黑名单
     */
    private static final String AUTH_BLACK = AuthFilterConfig.AUTH_REDIS_ROOT_KEY + "BLACK";

    /**
     * token过期后，10分钟不操作视为彻底过期，需要重新登录
     */
    private static final Long TIME_OUT = 10L;

    @Autowired
    private AuthFilterConfig authFilterConfig;

    @Autowired
    private CacheManager cacheManager;

    @Override
    public AuthUser userLogin(Object userId){
        return userLogin(userId, null);
    }

    @Override
    public AuthUser userLogin(Object userId, List<Object> userRoleIds){
        AuthUser authUser = new AuthUser();
        //用户ID
        authUser.setUserId(userId);
        //用户角色ID集合
        authUser.setRoleIds(userRoleIds);
        Date expDate = authFilterConfig.getExpDate();
        String token = JwtTools.createToken(userId, expDate);
        //token
        authUser.setToken(token);
        //超时时间
        authUser.setExpDate(expDate);
        //设置IP地址
        authUser.setIp(IpTools.getIpAddress(false));
        //登录时间
        authUser.setLoginDate(new Date());
        cacheManager.getCache(AUTH_USER).put(userId, authUser);
        return authUser;
    }

    @Override
    public <T> T getUserId(Class<T> clazz){
        Object userId = getObjectUserId();
        if (userId != null) {
            if (clazz.isInstance(userId)) {
                return clazz.cast(userId);
            }
            return (T) userId;
        } else {
            return null;
        }
    }

    @Override
    public AuthUser checkLogin(){
        AuthUser authUser = getAuthUser();
        if (authUser == null){
            throw new BusinessException(ApiResultEnum.LOGIN_EXPIRE.getStatus(), ApiResultEnum.LOGIN_EXPIRE.getMessage());
        }
        return authUser;
    }

    @Override
    public void loginOut() {
        Object userId = getObjectUserId();
        if (userId != null) {
            cacheManager.getCache(AUTH_USER).evict(userId);
            //是否存在用户信息
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null) {
                //清除
                new SecurityContextLogoutHandler().logout(this.getRequest(), this.getResponse(), authentication);
            }
        }
    }

    @Override
    public AuthUser tokenRefresh() {
        AuthUser authUser = getAuthUser();
        return tokenRefresh(authUser);
    }

    @Override
    public AuthUser tokenRefresh(AuthUser authUser) {
        if (authUser == null) {
            throw new BusinessException(ApiResultEnum.LOGIN_EXPIRE.getStatus(), ApiResultEnum.LOGIN_EXPIRE.getMessage());
        }
        Date expDate = authFilterConfig.getExpDate();
        //从新生成token给客户端
        String newToken = JwtTools.createToken(authUser.getUserId(), expDate);
        //token
        authUser.setToken(newToken);
        //超时时间
        authUser.setExpDate(expDate);
        //初始化时间
        authUser.setInitDate(new Date());
        cacheManager.getCache(AUTH_USER).put(authUser.getUserId(), authUser);
        return authUser;
    }

    @Override
    public AuthUser tokenValidate(){
        AuthUser authUser = getAuthUser();
        if (authUser == null){
            throw new BusinessException(ApiResultEnum.LOGIN_EXPIRE.getStatus(), ApiResultEnum.LOGIN_EXPIRE.getMessage());
        }
        //IP验证是否一致
        if (!IpTools.getIpAddress(false).equals(authUser.getIp())) {
            throw new BusinessException(ApiResultEnum.BLACK_LIST.getStatus(), ApiResultEnum.BLACK_LIST.getMessage());
        }
        //是否在黑名单中
        if (!isBlackToken(authUser)){
            throw new BusinessException(ApiResultEnum.BLACK_LIST.getStatus(), ApiResultEnum.BLACK_LIST.getMessage());
        }
        //token过期-重新生成（需判单是否满足重新生成规则）
        if (JwtTools.isExpired(authUser.getToken())){
            //距上次操作时间是否超时
            if (!checkLoginExpTime(authUser)) {
                throw BusinessException.error(ApiResultEnum.LOGIN_EXPIRE.getStatus(), ApiResultEnum.LOGIN_EXPIRE.getMessage());
            } else {
                //重新生成token给客户端
                authUser = tokenRefresh(authUser);
            }
        }
        //更新访问信息
        refreshAuthUser(authUser);
        return authUser;
    }

    @Override
    public List<Object> getUserRoles(){
        AuthUser authUser = getAuthUser();
        if (authUser == null){
            return null;
        }
        return authUser.getRoleIds();
    }

    @Override
    public boolean checkLoginExpTime(AuthUser authUser){
        if (authUser == null){
            return false;
        }
        if (authUser.getLoginDate() == null) {
            return false;
        }
        long from;
        if (authUser.getLastDate() == null) {
            from = authUser.getLoginDate().getTime();
        } else {
            from = authUser.getLastDate().getTime();
        }
        long to = new Date().getTime();
        return ((to - from) / (1000 * 60)) <= TIME_OUT;
    }

    /**
     * 获取用户ID Object类型
     * @return Object
     */
    private Object getObjectUserId(){
        return getRequest().getAttribute(authFilterConfig.getAuthUserIdKey());
    }

    /**
     * 获取缓存用户信息
     * @return T
     */
    private AuthUser getAuthUser() {
        Object userId = getObjectUserId();
        if (userId == null){
            return null;
        }
        return cacheManager.getCache(AUTH_USER).get(userId, AuthUser.class);
    }

    /**
     * Token验证通过后更新缓存
     * @param authUser 缓存对象
     */
    private void refreshAuthUser(AuthUser authUser){
        if (authUser != null){
            //有效访问次数+1
            authUser.setRefreshCount(authUser.getRefreshCount() + 1);
            //更新最后访问时间
            authUser.setLastDate(new Date());
            cacheManager.getCache(AUTH_USER).put(authUser.getUserId(), authUser);
        }
    }

    /**
     * 获取token
     * @return String
     */
    private String getToken(){
        return getRequest().getHeader(authFilterConfig.getAuthToken());
    }

    /**
     * 判断请求是否在黑名单内
     * @param authUser 缓存对象
     * @return boolean
     */
    private boolean isBlackToken(AuthUser authUser){
        AuthUser blackUser = cacheManager.getCache(AUTH_BLACK).get(authUser.getIp(), AuthUser.class);
        return blackUser == null;
    }
}
